#include "videostream_ffmpeg.h"

VSFFH264::VSFFH264()
{
    m_nVideoIndex = -1;
	m_nWidth = 0;
	m_nHeight = 0;
    m_pSwsContext = nullptr;
    m_pCodecContext = nullptr;
    av_register_all();
    m_uFrame = av_frame_alloc();
    m_uFrameBGR = av_frame_alloc();
}


VSFFH264::~VSFFH264() 
{
	av_frame_free(&m_uFrame);
	av_frame_free(&m_uFrameBGR);
	VSFrameQueue::Release();
	if(m_uOBuffer) {
		delete[] m_uOBuffer;
		m_uOBuffer = nullptr;
	}
}

bool
VSFFH264::Init()
{
	do {
		m_pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
		if(!m_pCodec) {
			break;
		}
		m_pCodecContext = avcodec_alloc_context3(m_pCodec);
		if(avcodec_open2(m_pCodecContext, m_pCodec, nullptr) < 0) {
			break;
		}
		return true;
	} while(0);
	return false;
}


void
VSFFH264::DecodeFrame(unsigned char* sPropBytes, int sPropLength,
                      unsigned char* pPropBytes, int pPropLength,
                      uint8_t* frameBuffer, int frameLength,
					  struct timeval frametime)
{
    //std::cout << "---------> "<< frameLength << std::endl;
    if(frameLength <= 20) return;
    unsigned char nalu_header[4] = {0x00, 0x00, 0x00, 0x01};
    int totalSize = 4 + sPropLength + 4 + pPropLength + 4 + frameLength;
    unsigned char* tmp = new unsigned char[totalSize];
    memcpy(tmp, nalu_header, 4);
    memcpy(tmp + 4, sPropBytes, sPropLength);
    memcpy(tmp + 4 + sPropLength, nalu_header, 4);
    memcpy(tmp + 4 + sPropLength + 4, pPropBytes, pPropLength);
    memcpy(tmp + 4 + sPropLength + 4 + pPropLength, nalu_header, 4);
    memcpy(tmp + 4 + sPropLength + 4 + pPropLength + 4,	frameBuffer, frameLength);
    int frameFinished = 0;
    AVPacket framePacket;
    av_init_packet(&framePacket);
    framePacket.size = totalSize;
    framePacket.data = tmp;
    do {
        int ret = avcodec_decode_video2(m_pCodecContext, m_uFrame, &frameFinished, &framePacket);
        if(ret < 0) {
            break;
        }
        if(frameFinished) {
			//printf("decodec_frame: w: %5d, h: %5d\n", m_uFrame->width, m_uFrame->height);
			int p_nWidth = m_uFrame->width;
			int p_nHeight = m_uFrame->height;
			if (m_nWidth == 0) {
				m_nWidth = p_nWidth;
				m_nHeight = p_nHeight;
				VSFrameQueue::Init(m_nWidth, m_nHeight);
			}
			else if(m_nHeight != p_nHeight)
			{
				m_nHeight = p_nHeight;
				m_nWidth = p_nHeight;
				VSFrameQueue::ReSize(m_nWidth, m_nHeight);
			}
            if(m_pSwsContext == nullptr) {
                m_pSwsContext = sws_getCachedContext(m_pSwsContext, p_nWidth, p_nHeight, AVPixelFormat::AV_PIX_FMT_YUV420P, p_nWidth, p_nHeight, AVPixelFormat::AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
                int size = avpicture_get_size(AV_PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);
                m_uOBuffer = (uint8_t* )av_malloc(size);
                avpicture_fill((AVPicture*)m_uFrameBGR, m_uOBuffer, AV_PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);
            }
            sws_scale(m_pSwsContext, (const uint8_t* const* )m_uFrame->data, m_uFrame->linesize, 0, p_nHeight, m_uFrameBGR->data, m_uFrameBGR->linesize);
            VSFrameQueue::Push(m_uOBuffer);
        }
    } while(0);
    av_free_packet(&framePacket);
    delete[] tmp;
    tmp = nullptr; //防止产生悬垂指针使程序产生没必要的错误
}

int
VSFFH264::GetDecodeFrame(cv::Mat* frame)
{
	*frame = VSFrameQueue::Pop();
	return 0;
}

